﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Windows;
using Microsoft.Win32;
using System.Collections.ObjectModel;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.IO;
using System.Net;
using static DW.Library.Win32API;
using System.Management;

namespace DW.Library
{
    /// <summary>
    /// 进程相关操作
    /// </summary>
    public class ProcessUtil
    {


        private static Dictionary<string, string> _errProcessDict = new Dictionary<string, string>();
        private static Dictionary<string, BitmapSource> _icoDict = new Dictionary<string, BitmapSource>();
        public static async Task<List<AppInfo>> GetAllWindowsAppsAsync()
        {
            return await Task.Run(() =>
              {
                  List<AppInfo> apps = new List<AppInfo>();
                  List<RegistryKey> RegistryKeys = new List<RegistryKey>();
                  RegistryKeys.Add(Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"));
                  RegistryKeys.Add(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"));
                  RegistryKeys.Add(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"));
                  foreach (var item in RegistryKeys)
                  {
                      // 打开注册表中的Uninstall键
                      using (RegistryKey key = item)
                      {
                          if (key != null)
                          {
                              // 获取所有子键的名称，即安装的应用程序的标识符
                              string[] subKeyNames = key.GetSubKeyNames();

                              // 遍历所有子键，并输出应用程序的名称
                              foreach (string subKeyName in subKeyNames)
                              {

                                  using (RegistryKey regSub = key.OpenSubKey(subKeyName))
                                  {
                                      AppInfo appInfo = new AppInfo();
                                      string displayName = regSub.GetValue("DisplayName") as string;
                                      string installLocation = regSub.GetValue("InstallLocation") as string;
                                      string uninstallString = regSub.GetValue("UninstallString") as string;
                                      string displayIcon = regSub.GetValue("DisplayIcon") as string;
                                      int systemComponent = (int)regSub.GetValue("SystemComponent", 0);
                                      if (string.IsNullOrWhiteSpace(displayName))
                                      {
                                          continue;
                                      }
                                      if (string.IsNullOrWhiteSpace(uninstallString))
                                      {
                                          continue;
                                      }
                                      if (string.IsNullOrWhiteSpace(displayIcon))
                                      {
                                          continue;
                                      }
                                      if (systemComponent == 1) // 排除系统应用
                                      {
                                          continue;
                                      }
                                      appInfo.ProcessName = displayName;
                                      appInfo.ExePath = installLocation;
                                      appInfo.IconPath = displayIcon;
                                      apps.Add(appInfo);
                                  }

                              }
                          }
                      }
                  }
                  return apps;
              });

        }
        /// <summary>
        /// 获取所有的进程，如果进程名称一致，则只取第一次进程。
        /// 因此使用HashSet来去除重复的
        /// </summary>
        /// <returns></returns>
        public static async Task<List<AppInfo>> GetAllProcessListAsync(bool loadicon = false)
        {
            List<AppInfo> processList = new List<AppInfo>();
            HashSet<string> hashSet = new HashSet<string>();
            Process[] processes = Process.GetProcesses();
            for (int i = 0; i < processes.Length; i++)
            {
                //只考虑第一个进程名
                if (hashSet.Add(processes[i].ProcessName))
                {
                    DateTime startTime = DateTime.MinValue;
                    await Task.Run(() =>
                    {
                        try
                        {
                            startTime = processes[i].StartTime;
                        }
                        catch (System.ComponentModel.Win32Exception)
                        {
                            startTime = DateTime.MinValue;
                        }
                    });
                    var app = new AppInfo() { ProcessName = processes[i].ProcessName, Process = processes[i], StartTime = startTime };
                    try
                    {
                        app.ExePath = processes[i].MainModule.FileName;
                    }
                    catch (Exception)
                    {


                    }
                    if (loadicon)
                    {
                        app.Icon = await GetProcessIcon(processes[i].MainModule?.FileName);
                    }
                    processList.Add(app);
                }
            }
            // 使用最近使用时间对进程列表进行排序
            processList = processList.OrderByDescending(p => p.StartTime).ToList();
            return processList;
        }

        public static async Task<IntPtr> GetHwndByProcessId(int processId)
        {
            try
            {
                List<AppInfo> appInfos = await GetAllWindowListAsync(null);
                AppInfo app = appInfos.Where(x => x.Process.Id == processId).FirstOrDefault();
                if (app != null)
                {
                    return app.WindowInfo.Hwnd;
                }
            }
            catch (Exception ex)
            {
                Logger.Instance.Error("ProcessUtil", "GetHwndByProcessId", $"exception:{ex.Message}");
            }

            return IntPtr.Zero;
        }

        public static async Task<List<AppInfo>> GetAllWindowListAsync(List<AppInfo> appInfos, bool loadicon = false)
        {
            List<AppInfo> processList = new List<AppInfo>();
            HashSet<string> hashSet = new HashSet<string>();
            var windows = await WindowInfo.FindAllWindows();
            List<Process> processes = new List<Process>();
            foreach (var win in windows)
            {
                //if (appInfos != null)
                //{
                //    List<AppInfo> existApps = appInfos.Where(x => x.Managed.Hwnd == win.Hwnd).ToList();
                //    if (existApps.Count > 0)
                //    {
                //        processList.Add(existApps[0]);
                //        continue;
                //    }
                //}
                GetWindowThreadProcessId(win.Hwnd, out int id);
                var pro = Process.GetProcessById(id);
                if (Process.GetCurrentProcess().ProcessName == pro.ProcessName) continue;

                DateTime startTime = DateTime.MinValue; ;
                await Task.Run(() =>
                {
                    try
                    {
                        startTime = pro.StartTime;
                    }
                    catch (System.ComponentModel.Win32Exception)
                    {
                        startTime = DateTime.MinValue;
                    }
                });
                bool hasElevated = ProcessHasElevatedPrivileges(pro);
                string processName = pro.ProcessName;
                //string ExePath = pro.MainModule.FileName;
                bool isUWP = false;
                string ExePath = GetExecutablePath(win.Hwnd, id);

                if (ExePath.StartsWith(@"C:\Program Files\WindowsApps"))
                {
                    isUWP = true;
                }
                if (pro.ProcessName == "ApplicationFrameHost" && !string.IsNullOrEmpty(pro.MainWindowTitle))
                {
                    processName = pro.MainWindowTitle;
                    //ExePath = GetExecutablePath(win.Hwnd, id);
                }
                var app = new AppInfo() { ProcessName = processName, Process = pro, StartTime = startTime };
                try
                {
                    app.WindowInfo = win;
                    app.ExePath = ExePath;
                    app.Managed = new AppInfo.HwndManaged();
                    app.Managed.Hwnd = app.WindowInfo.Hwnd;
                    app.HasElevated = hasElevated;
                    app.Index = processList.Count;
                    //app.Managed.Set(app.WindowInfo.Hwnd);
                }
                catch (Exception)
                {


                }
                if (loadicon)
                {
                    app.Icon = await GetProcessIcon(ExePath);
                }
                processList.Add(app);
            }
            // 使用最近使用时间对进程列表进行排序
            processList = processList.OrderByDescending(p => p.StartTime).ToList();
            return processList;

        }

        public static async Task<List<AppInfo>> GetAllWin32WindowListAsync(List<AppInfo> appInfos, bool loadicon = false)
        {
            List<AppInfo> processList = new List<AppInfo>();
            processList = await GetAllWindowListAsync(appInfos, loadicon);
            processList = processList.Where(x => x.WindowInfo.ClassName != "Windows.UI.Core.CoreWindow" && x.WindowInfo.ClassName != "Progman" && x.WindowInfo.ClassName != "Xaml_WindowedPopupClass" && x.WindowInfo.ClassName != "TopLevelWindowForOverflowXamlIsland").ToList();
            return processList;

        }
        private static bool ProcessHasElevatedPrivileges(Process process)
        {
            try
            {
                string key = $"{process.Id}_{process.ProcessName}";
                if (_errProcessDict.ContainsKey(key))
                {
                    return true;
                }
                if (Environment.OSVersion.Version.Major >= 6)
                {
                    IntPtr hObject = process.Handle;
                    return IsProcessElevated(hObject);
                }
                return false;
            }
            catch
            {
                string key = $"{process.Id}_{process.ProcessName}";
                if (!_errProcessDict.ContainsKey(key))
                {
                    _errProcessDict.Add(key, null);
                }
                return true;
            }

        }

        private static bool IsProcessElevated(IntPtr hProcess)
        {
            if (!OpenProcessToken(hProcess, 0x20 | 0x0008, out IntPtr hToken))
            {
                throw new Win32Exception();
            }

            var elevation = default(TOKEN_ELEVATION);  // 初始化 elevation
            try
            {
                if (!GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevation, ref elevation, Marshal.SizeOf(typeof(TOKEN_ELEVATION)), out var returnLength))
                {
                    throw new Win32Exception();
                }

                return elevation.TokenIsElevated != 0;
            }
            finally
            {
                CloseHandle(hToken);
            }
        }

        private static string GetExecutablePath(IntPtr hWnd, int pid)
        {
            var buffer = new StringBuilder(1024);
            IntPtr hprocess = OpenProcess(ProcessAccessFlags.ProcessQueryLimitedInformation, false, (int)pid);
            if (hprocess != IntPtr.Zero)
            {
                try
                {
                    int size = buffer.Capacity;
                    if (QueryFullProcessImageName(hprocess, 0, buffer, out size))
                    {
                        var path = buffer.ToString();
                        if (path == @"C:\Windows\System32\ApplicationFrameHost.exe")
                        {
                            path = GetModernAppExecutablePath(hWnd, pid);
                            return path;
                        }
                        return buffer.ToString();
                    }
                }
                finally
                {
                    CloseHandle(hprocess);
                }
            }
            return string.Empty;
        }

        private static string GetModernAppExecutablePath(IntPtr hWnd, int pid)
        {
            var children = GetChildWindows(hWnd);
            foreach (var childHwnd in children)
            {
                int childPid = 0;
                GetWindowThreadProcessId(childHwnd, out childPid);
                if (childPid != pid)
                {
                    return GetExecutablePath(childHwnd, childPid);
                }
            }
            return "";
        }

        private static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumChildWindows(parent, new EnumWindowsProc(EnumWindow), GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }
        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                //throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
                return false;
            }
            list.Add(handle);
            return true;
        }
        /// <summary>
        /// 获取进程对应的用户名
        /// </summary>
        /// <param name="pID"></param>
        /// <returns></returns>
        public static string GetProcessUserName(int pID)
        {
            string userName = string.Empty;
            try
            {
                foreach (ManagementObject item in new ManagementObjectSearcher("Select * from Win32_Process WHERE processID=" + pID).Get())
                {
                    ManagementBaseObject inPar = null;
                    ManagementBaseObject outPar = null;

                    inPar = item.GetMethodParameters("GetOwner");
                    outPar = item.InvokeMethod("GetOwner", inPar, null);
                    userName = Convert.ToString(outPar["User"]);
                    break;
                }
            }
            catch
            {
                userName = "SYSTEM";
            }
            return userName;
        }

        /// <summary>
        /// 打印当前选中的进程信息
        /// </summary>
        /// <param name="process"></param>
        /// <returns></returns>
        public static string GetProcessMessage(Process? process)
        {
            if (process == null || process.HasExited) return "";
            StringBuilder sb = new StringBuilder();
            try
            {
                sb.Append($"进程名称：{process.ProcessName}\n");
                sb.Append($"进程ID：{process.Id}\n");
                sb.Append($"用户名称：{GetProcessUserName(process.Id)}\n");
                sb.Append($"模块(映像)名称：{process.MainModule.ModuleName}\n");
                sb.Append($"文件路径：{process.MainModule.FileName}\n");
                sb.Append($"进程描述：{process.MainModule.FileVersionInfo.FileDescription}\n");
                sb.Append($"启动线程数：{process.Threads.Count}\n");
                sb.Append($"CPU占用时间：{process.TotalProcessorTime}\n");
                sb.Append($"线程优先级：{process.PriorityClass}\n");
                sb.Append($"启动时间：{process.StartTime}\n");
                sb.Append($"专用内存：{process.PrivateMemorySize64 / 1024}KB\n");
                sb.Append($"峰值虚拟内存：{process.PeakVirtualMemorySize64 / 1024}KB\n");
                sb.Append($"峰值分页内存：{process.PagedSystemMemorySize64 / 1024}KB\n");
                sb.Append($"分页系统内存：{process.PagedMemorySize64 / 1024}KB\n");
                sb.Append($"分页内存：{process.PagedMemorySize64 / 1024}KB\n");
                sb.Append($"未分页系统内存：{process.NonpagedSystemMemorySize64 / 1024}KB\n");
                sb.Append($"物理内存：{process.WorkingSet64 / 1024}KB\n");
                sb.Append($"虚拟内存：{process.VirtualMemorySize64 / 1024}KB\n");
                var cpuUsage = GetProcessCpu(process);
                var memoryUsage = GetProcessMemory(process); ;
                var diskUsage = GetProcessDisk(process);
                sb.Append($"CPU使用率: {cpuUsage}%\n");
                sb.Append($"内存使用量: {memoryUsage / (1024 * 1024)} MB\n");
                sb.Append($"磁盘读写速率: {diskUsage}bytes/second");
            }
            catch (Exception ex)
            {
                sb.Append($"访问或操作失败：{ex.Message}\n");
            }
            return sb.ToString();
        }
        public static async Task<BitmapSource> GetIcon(string fileName)
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(fileName))
                {
                    if (_icoDict.ContainsKey(fileName))
                    {
                        return _icoDict[fileName];
                    }
                    var ico = await Task.Run(() =>
                    {
                        return File.Exists(fileName) ? Icon.ExtractAssociatedIcon(fileName) : null;
                    });
                    BitmapSource result = null;
                    if (ico != null)
                    {
                        result = Imaging.CreateBitmapSourceFromHIcon(ico.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                    }
                    if (!_icoDict.ContainsKey(fileName))
                    {
                        _icoDict.Add(fileName, result);
                    }
                    return result;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception)
            {

                return null;
            }

        }

        public static BitmapSource GetIconFromUri(string imagePath)
        {
            try
            {
                BitmapImage bitmapImage = new BitmapImage(new Uri(imagePath));
                return bitmapImage;
            }
            catch (Exception ex)
            {
                Logger.Instance.Info("ProcessUtil", "GetIconFromUri", ex.Message);
                return null;
            }
        }
        public static async Task<BitmapSource> GetProcessIcon(string? exePath)
        {
            try
            {
                if (!string.IsNullOrEmpty(exePath) && File.Exists(exePath))
                {
                    return await GetIcon(exePath);
                }
                else
                {
                    return null;
                }

            }
            catch (Exception)
            {

                return null;
            }

        }
        public static async Task<string> GetProcessCounterMessage(Process? process)
        {
            return await Task.Run(() =>
            {
                StringBuilder sb = new StringBuilder();
                if (process == null) return "";
                try
                {
                    var cpuUsage = GetProcessCpu(process);
                    var memoryUsage = GetProcessMemory(process); ;
                    var diskUsage = GetProcessDisk(process);
                    sb.Append($"CPU使用率: {cpuUsage}%\n");
                    sb.Append($"内存使用量: {memoryUsage / (1024 * 1024)} MB\n");
                    sb.Append($"磁盘读写速率: {diskUsage}bytes/second");
                }
                catch (Exception ex)
                {

                    sb.Append("获取内存信息失败" + ex.Message);
                }
                return sb.ToString();
            });

        }
        public static double GetProcessCpu(Process process)
        {
            return GetCpuUsageForProcess(process).Result;
            if (process == null) return 0;
            var value = new PerformanceCounter("Process", "% Processor Time", process.ProcessName).NextValue();
            return value / Environment.ProcessorCount;
        }
        private static async Task<double> GetCpuUsageForProcess(Process process)
        {
            if (process == null || process.HasExited) return 0;
            var startTime = DateTime.UtcNow;
            var startCpuUsage = process.TotalProcessorTime;

            await Task.Delay(1000);

            var endTime = DateTime.UtcNow;
            var endCpuUsage = process.TotalProcessorTime;

            var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds;
            var totalMsPassed = (endTime - startTime).TotalMilliseconds;

            var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed);

            return cpuUsageTotal * 100;
        }
        public static double GetProcessMemory(Process process, MemoryType memoryType = MemoryType.PrivateBytes)
        {
            if (process == null || process.HasExited) return 0;
            string str = "Working Set - Private";
            switch (memoryType)
            {
                case MemoryType.HandleCount:
                    str = "Handle Count";
                    break;
                case MemoryType.PrivateBytes:
                    str = "Working Set - Private";
                    break;
                case MemoryType.WorkingSet:
                    str = "Working Set";
                    break;
                case MemoryType.VirtualBytes:
                    str = "Virtual Bytes";
                    break;
                default:
                    break;
            }
            return new PerformanceCounter("Process", str, process.ProcessName).NextValue();
        }
        public static double GetProcessDisk(Process process)
        {
            if (process == null || process.HasExited) return 0;
            return new PerformanceCounter("Process", "IO Data Operations/sec", process.ProcessName).NextValue();
        }

        /// <summary>
        /// Create VirtualDisplay and return window handle
        /// </summary>
        /// <returns></returns>
        public static async Task<IntPtr> WaitVirtualDisplayApplication(string processName)
        {
            try
            {
                int count = 10;
                IntPtr hwnd = IntPtr.Zero;
                while (hwnd == IntPtr.Zero && count > 0)
                {
                    Logger.Instance.Error("ProcessUtil", "WaitVirtualDisplayApplication", $"wait VD {count} start");
                    await Task.Delay(1000);

                    List<AppInfo> listBoxItemAppInfos = await GetAllWin32WindowListAsync(null);
                    AppInfo firstAppInfo = listBoxItemAppInfos.Where(x => x.ProcessName == processName).FirstOrDefault();
                    if (firstAppInfo != null)
                    {
                        hwnd = firstAppInfo.Managed.Hwnd;
                    }
                    count--;
                    Logger.Instance.Error("ProcessUtil", "WaitVirtualDisplayApplication", $"wait VD {count} end");
                }
                return hwnd;
            }
            catch (Exception ex)
            {
                Logger.Instance.Error("ProcessUtil", "OpenThridApplication", $"ex:{ex.Message}");
                return IntPtr.Zero;
            }

        }

        /// <summary>
        /// get class name
        /// </summary>
        /// <param name="hwnd"></param>
        public static string GetClassName(IntPtr hwnd)
        {
            StringBuilder className = new StringBuilder(256);
            Win32API.GetClassName(hwnd, className, className.Capacity); // 获取窗体所在程序类名
            string class_Name = className.ToString();
            return class_Name;
        }
    }
    public enum MemoryType
    {
        HandleCount,
        PrivateBytes,
        WorkingSet,
        VirtualBytes
    }
}
